package cn.iocoder.yudao.module.aiposter.service.poster;

import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.aiposter.controller.app.poster.vo.req.*;
import cn.iocoder.yudao.module.aiposter.controller.app.poster.vo.resp.ImgRenderResp;
import cn.iocoder.yudao.module.aiposter.controller.app.poster.vo.resp.PosterGenerateRespVO;
import cn.iocoder.yudao.module.aiposter.controller.app.submit.vo.AppSubmitSaveReqVO;
import cn.iocoder.yudao.module.aiposter.controller.app.vec.vo.VecTemplateMixSimilaritySearchReqVO;
import cn.iocoder.yudao.module.aiposter.controller.app.vec.vo.VecTemplateSimilaritySearchRespVO;
import cn.iocoder.yudao.module.aiposter.dal.dataobject.poster.PosterDO;
import cn.iocoder.yudao.module.aiposter.dal.dataobject.template.AiParamBO;
import cn.iocoder.yudao.module.aiposter.enums.ErrorCodeConstants;
import cn.iocoder.yudao.module.aiposter.queue.Result;
import cn.iocoder.yudao.module.aiposter.queue.TaskToolExecutor;
import cn.iocoder.yudao.module.aiposter.queue.Worker;
import cn.iocoder.yudao.module.aiposter.queue.draw.DrawResult;
import cn.iocoder.yudao.module.aiposter.queue.draw.DrawCommand;
import cn.iocoder.yudao.module.aiposter.queue.draw.DrawTypeEnum;
import cn.iocoder.yudao.module.aiposter.remote.DrawClient;
import cn.iocoder.yudao.module.aiposter.remote.vo.SegmentationParam;
import cn.iocoder.yudao.module.aiposter.service.poster.bo.*;
import cn.iocoder.yudao.module.aiposter.dal.dataobject.template.TemplateDO;
import cn.iocoder.yudao.module.aiposter.dal.mysql.poster.AppPosterMapper;
import cn.iocoder.yudao.module.aiposter.remote.AiClient;
import cn.iocoder.yudao.module.aiposter.remote.ImgClient;
import cn.iocoder.yudao.module.aiposter.remote.vo.AiSynthesisResp;
import cn.iocoder.yudao.module.aiposter.remote.vo.AiSynthesisRqe;
import cn.iocoder.yudao.module.aiposter.service.submit.AppSubmitService;
import cn.iocoder.yudao.module.aiposter.service.template.AppTemplateService;
import cn.iocoder.yudao.module.aiposter.service.vec.VecService;
import cn.iocoder.yudao.module.aiposter.utils.CommonUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Decoder;

import javax.annotation.Nullable;
import javax.annotation.Resource;
import java.io.IOException;
import java.nio.file.Path;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.aiposter.enums.ErrorCodeConstants.POSTER_NOT_EXISTS;

@Service
@Validated
@Slf4j
public class AppPosterServiceImpl implements AppPosterService {
    @Resource
    private AppPosterMapper appPosterMapper;

    @Resource
    private AppTemplateService appTemplateService;

    @Resource
    private AppSubmitService appSubmitService;

    @Resource
    private VecService vecService;

    @Resource
    private AiClient aiClient;

    @Resource
    private ImgClient imgClient;

    @Resource
    private DrawClient drawClient;

    @Resource
    private TaskToolExecutor drawExecutor;

    @Override
    public byte[] segmentation(MultipartFile multipartFile) {
        try {
            // 获取上传文件的base64
            String base64 = CommonUtils.getImgBase64(multipartFile);
            // AI抠图
            List<AiSynthesisRqe> requestItem = new ArrayList<>();
            AiSynthesisRqe item = new AiSynthesisRqe();
            item.setImgBase64(base64);
            requestItem.add(item);
            SegmentationParam param = new SegmentationParam();
            param.setSegPattern("all");
            List<AiSynthesisResp> respList = aiClient.requestSegmentation(requestItem, param);
            String resultB64 = respList.get(0).getBase64();
            BASE64Decoder decoder = new BASE64Decoder();
            // 当前仅处理一张图片
            return decoder.decodeBuffer(resultB64);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ServiceException(ErrorCodeConstants.IMG_UPLOAD_ERROR);
        }
    }

    @Override
    public String renderImg(AppPosterRenderReqVO reqVO) {
        // 存在url使用url渲染
        // 不存在url使用base64上传文件后设置url进行渲染
        if (reqVO.getImgBase64() != null) {
            String imgUrl = imgClient.uploadSegmentationBase64(reqVO.getImgBase64(), "png");
            reqVO.setImgUrl(imgUrl);
        }

        TemplateDO templateDO = appTemplateService.getTemplate(reqVO.getTemplateId());

        ImgDataInfoBO imgDataInfoBO = JsonUtils.parseObject(templateDO.getData(), ImgDataInfoBO.class);
        ImgPageBO imgDataPage = imgDataInfoBO.getPage();
        List<ImgWidgetBO> imgWidgetBOList = imgDataInfoBO.getWidgets();
        if (reqVO.getType().equals(ImgRenderTypeEnum.AI_COMPOSE)) {
            // AI在当前场景是占位符，渲染时候需要删除它
            imgWidgetBOList.removeIf(item -> item.getIsAI() && item.getType() == ImgWidgetTypeEnum.IMG);
            if (reqVO.getImgUrl() != null) {
                imgDataPage.setBackgroundImage(reqVO.getImgUrl());
            }
        } else {
            // 抠图仅替换当前图片
            imgWidgetBOList.forEach(item -> {
                if (item.getIsSegmentation()) {
                    item.setImgUrl(reqVO.getImgUrl());
                }
            });
        }

        PosterDO posterDO = createPosterDBByTemplate(templateDO);
        posterDO.setData(JsonUtils.toJsonString(imgDataInfoBO));
        appPosterMapper.insert(posterDO);

        Path localCoverPath = drawClient.coverScreenshot(posterDO.getId());
        // TODO 可能在某些情况下海报封面生成失败，后续可以有定时任务监控
        if (null != localCoverPath) {
            String remotePath = imgClient.uploadCover(localCoverPath.toString());
            posterDO.setCover(remotePath);
            appPosterMapper.updateById(posterDO);
        }
        Path localPosterPath = drawClient.posterScreenshot(posterDO.getId(), posterDO.getWidth(), posterDO.getHeight());
        return imgClient.uploadPoster(localPosterPath.toString());
    }

    @Override
    public List<ImgRenderResp> renderImagesAsync(List<AppPosterRenderReqVO> renderReqVOS) {
        Map<Long, Future<Result<DrawResult>>> coverFutureMap = new HashMap<>();
        Map<Long, Future<Result<DrawResult>>> posterFutureMap = new HashMap<>();
        Map<Long, PosterDO> posterDOMap = new HashMap<>();
        for (AppPosterRenderReqVO reqVO : renderReqVOS) {
            // 存在url使用url渲染
            // 不存在url使用base64上传文件后设置url进行渲染
            if (reqVO.getImgBase64() != null) {
                String imgUrl = imgClient.uploadSegmentationBase64(reqVO.getImgBase64(), "png");
                reqVO.setImgUrl(imgUrl);
            }

            TemplateDO templateDO = appTemplateService.getTemplate(reqVO.getTemplateId());

            ImgDataInfoBO imgDataInfoBO = JsonUtils.parseObject(templateDO.getData(), ImgDataInfoBO.class);
            ImgPageBO imgDataPage = imgDataInfoBO.getPage();
            List<ImgWidgetBO> imgWidgetBOList = imgDataInfoBO.getWidgets();
            if (reqVO.getType().equals(ImgRenderTypeEnum.AI_COMPOSE)) {
                // AI在当前场景是占位符，渲染时候需要删除它
                imgWidgetBOList.removeIf(item -> item.getIsAI() && item.getType() == ImgWidgetTypeEnum.IMG);
                if (reqVO.getImgUrl() != null) {
                    imgDataPage.setBackgroundImage(reqVO.getImgUrl());
                }
            } else {
                // 抠图仅替换当前图片
                imgWidgetBOList.forEach(item -> {
                    if (item.getIsSegmentation()) {
                        item.setImgUrl(reqVO.getImgUrl());
                    }
                });
            }

            PosterDO posterDO = createPosterDBByTemplate(templateDO);
            posterDO.setData(JsonUtils.toJsonString(imgDataInfoBO));
            appPosterMapper.insert(posterDO);
            posterDOMap.put(posterDO.getId(), posterDO);

            // 异步封面生成
            Worker<DrawResult> coverWorker = new Worker<>();
            coverWorker.setCommand(new DrawCommand(coverWorker, posterDO, DrawTypeEnum.COVER));
            Future<Result<DrawResult>> coverResultFuture = drawExecutor.submitWithFuture(coverWorker);
            coverFutureMap.put(posterDO.getId(), coverResultFuture);
            // 异步海报生成
            Worker<DrawResult> posterWorker = new Worker<>();
            posterWorker.setCommand(new DrawCommand(posterWorker, posterDO, DrawTypeEnum.POSTER));
            Future<Result<DrawResult>> posterResultFuture = drawExecutor.submitWithFuture(posterWorker);
            posterFutureMap.put(posterDO.getId(), posterResultFuture);
        }
        // 等待结果
        long start = System.currentTimeMillis();
        boolean isDrawProcess = true;
        while (isDrawProcess) {
            isDrawProcess = false;
            for (Future<Result<DrawResult>> futureItem : coverFutureMap.values()) {
                if (!futureItem.isDone()) {
                    isDrawProcess = true;
                }
            }
            for (Future<Result<DrawResult>> futureItem : posterFutureMap.values()) {
                if (!futureItem.isDone()) {
                    isDrawProcess = true;
                }
            }
        }
        long end = System.currentTimeMillis();
        log.info("生成海报{}耗时（毫秒）：{}", StringUtils.join(",", posterDOMap.keySet().toArray()), end - start);
        // 返回结果
        List<ImgRenderResp> respList = new ArrayList<>();
        for (Long posterId : posterDOMap.keySet()) {
            PosterDO posterDO = posterDOMap.get(posterId);
            Future<Result<DrawResult>> coverFuture = coverFutureMap.get(posterId);
            Result<DrawResult> drawResult;
            try {
                drawResult = coverFuture.get();
                posterDO.setCover(drawResult.value.getImgUrl());
                appPosterMapper.updateById(posterDO);
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }

            Future<Result<DrawResult>> posterFuture = posterFutureMap.get(posterId);
            Result<DrawResult> posterResult;
            try {
                posterResult = posterFuture.get();
                ImgRenderResp resultItem = ImgRenderResp.builder()
                        .posterUrl(posterResult.value.getImgUrl())
                        .templateId(posterDO.getTemplateId())
                        .build();
                respList.add(resultItem);
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            } catch (ServiceException e1) {
                ImgRenderResp resultItem = ImgRenderResp.builder()
                        .posterUrl(e1.getMessage())
                        .templateId(posterDO.getTemplateId())
                        .build();
                respList.add(resultItem);
            }
        }
        return respList;
    }

    @Override
    public List<ImgRenderResp> renderImagesSync(List<AppPosterRenderReqVO> renderReqVOS) {
        List<ImgRenderResp> respList = new ArrayList<>();
        for (AppPosterRenderReqVO reqVO : renderReqVOS) {
            // 存在url使用url渲染
            // 不存在url使用base64上传文件后设置url进行渲染
            if (reqVO.getImgBase64() != null) {
                String imgUrl = imgClient.uploadSegmentationBase64(reqVO.getImgBase64(), "png");
                reqVO.setImgUrl(imgUrl);
            }

            TemplateDO templateDO = appTemplateService.getTemplate(reqVO.getTemplateId());

            ImgDataInfoBO imgDataInfoBO = JsonUtils.parseObject(templateDO.getData(), ImgDataInfoBO.class);
            ImgPageBO imgDataPage = imgDataInfoBO.getPage();
            List<ImgWidgetBO> imgWidgetBOList = imgDataInfoBO.getWidgets();
            if (reqVO.getType().equals(ImgRenderTypeEnum.AI_COMPOSE)) {
                // AI在当前场景是占位符，渲染时候需要删除它
                imgWidgetBOList.removeIf(item -> item.getIsAI() && item.getType() == ImgWidgetTypeEnum.IMG);
                if (reqVO.getImgUrl() != null) {
                    imgDataPage.setBackgroundImage(reqVO.getImgUrl());
                }
            } else {
                // 抠图仅替换当前图片
                imgWidgetBOList.forEach(item -> {
                    if (item.getIsSegmentation()) {
                        item.setImgUrl(reqVO.getImgUrl());
                    }
                });
            }

            PosterDO posterDO = createPosterDBByTemplate(templateDO);
            posterDO.setData(JsonUtils.toJsonString(imgDataInfoBO));
            appPosterMapper.insert(posterDO);

            Path localCoverPath = drawClient.coverScreenshot(posterDO.getId());
            // TODO 可能在某些情况下海报封面生成失败，后续可以有定时任务监控
            if (null != localCoverPath) {
                String remotePath = imgClient.uploadCover(localCoverPath.toString());
                posterDO.setCover(remotePath);
                appPosterMapper.updateById(posterDO);
            }
            Path localPosterPath = drawClient.posterScreenshot(posterDO.getId(), posterDO.getWidth(), posterDO.getHeight());
            String resultImg = imgClient.uploadPoster(localPosterPath.toString());

            ImgRenderResp resultItem = ImgRenderResp.builder()
                    .posterUrl(resultImg)
                    .templateId(posterDO.getTemplateId())
                    .build();
            respList.add(resultItem);
        }
        return respList;
    }

    @Override
    public List<PosterGenerateRespVO> generatePoster(PosterGenerateReqVO generateReqVO) {
        // 定义返回结果
        List<PosterDO> posterResult = new ArrayList<>();
        List<Long> posterIds = new ArrayList<>();

        // 1.根据行业和关键词召回模版id 3个AI一个Seg
        VecTemplateMixSimilaritySearchReqVO vecReq = VecTemplateMixSimilaritySearchReqVO.builder()
                .bizName(generateReqVO.getBizName())
                .keyword(generateReqVO.getKeyword())
                .build();
        List<VecTemplateSimilaritySearchRespVO> similaritySearchItems = vecService.similaritySearchMix(vecReq);
        List<Long> templateIds = similaritySearchItems.stream()
                .map(VecTemplateSimilaritySearchRespVO::getTemplateId)
                .collect(Collectors.toList());

        // 2. 根据模版ID从数据库找到对应模版
        List<TemplateDO> templates = appTemplateService.getTemplates(templateIds);
        // 3. 获取每个模版所需AI生成的信息，构建成一个新的JSON数据
        for (TemplateDO template : templates) {
            ImgDataInfoBO imgDataInfoBO = JsonUtils.parseObject(template.getData(), ImgDataInfoBO.class);
            ImgPageBO imgDataPage = imgDataInfoBO.getPage();
            List<ImgWidgetBO> imgWidgetBOList = imgDataInfoBO.getWidgets();
            AiParamBO aiParamBO = JsonUtils.parseObject(template.getAiParam(), AiParamBO.class);

            List<AiSynthesisRqe> aiImgList = new ArrayList<>();
            List<AiSynthesisRqe> aiTextList = new ArrayList<>();
            List<AiSynthesisRqe> aiBgList = new ArrayList<>();
            List<AiSynthesisRqe> aiSegList = new ArrayList<>();

            Map<String, AiSynthesisResp> synthesisUidMap = new HashMap<>();
            imgWidgetBOList.forEach(item -> {
                if (item.getIsAI()) {
                    if (item.getType() == ImgWidgetTypeEnum.IMG && StringUtils.isNotEmpty(generateReqVO.getImageUrl())) {
                        // 用户上传的图片不为空则合成图片
                        AiSynthesisRqe reqItem = BeanUtils.toBean(item, AiSynthesisRqe.class);
                        // 额外增加page信息
                        reqItem.setPageWidth(imgDataPage.getWidth());
                        reqItem.setPageHeight(imgDataPage.getHeight());
                        reqItem.setBizName(generateReqVO.getBizName());
                        reqItem.setKeyword(generateReqVO.getKeyword());
                        // 增加AI高级参数
                        if (aiParamBO != null) {
                            reqItem.setPrompt(aiParamBO.getPrompt());
                            reqItem.setNegativePrompt(aiParamBO.getNegativePrompt());
                            reqItem.setStopAt(aiParamBO.getStopAt());
                            reqItem.setWeight(aiParamBO.getWeight());
                            reqItem.setGuidanceScale(aiParamBO.getGuidanceScale());
                        }
                        // 防止只有背景没有图片的模版混入
                        if (StringUtils.isNotEmpty(imgDataPage.getBackgroundImage())) {
                            reqItem.setPageBgImg(imgDataPage.getBackgroundImage());
                            reqItem.setImgUrl(generateReqVO.getImageUrl());
                            aiImgList.add(reqItem);
                        }
                    } else if (item.getType() == ImgWidgetTypeEnum.TEXT) {
                        aiTextList.add(BeanUtils.toBean(item, AiSynthesisRqe.class));
                    }
                }
                if (item.getIsSegmentation() && StringUtils.isNotEmpty(generateReqVO.getImageUrl())) {
                    aiSegList.add(BeanUtils.toBean(item, AiSynthesisRqe.class));
                }
            });
            // add. 额外增加AI生成背景图
            if (imgDataPage.getIsAI()) {
                AiSynthesisRqe bgReq = BeanUtils.toBean(imgDataPage, AiSynthesisRqe.class);
                aiBgList.add(bgReq);
            }

            // 4. 根据DSL中图文信息生成AI相关内容

            // 4.1 请求AI生图接口生成指定背景图
            log.info("正在处理 templateId: {} AI生成背景", template.getId());
            List<AiSynthesisResp> aiSynthesisBg = aiClient.requestBgAi(aiBgList);
            log.info("正在处理 templateId: {} AI生成背景, {} 个", template.getId(), aiSynthesisBg.size());

            // 4.2 请求AI生图接口生成指定图片
            log.info("正在处理 templateId: {} AI生成图片", template.getId());
            List<AiSynthesisResp> aiSynthesisImages = requestAIImg(aiImgList, generateReqVO, imgDataPage);
            log.info("正在处理 templateId: {} AI生成图片, {} 个", template.getId(), aiSynthesisImages.size());

            // 4.3 请求AI生文接口生成合适的文本
            log.info("正在处理 templateId: {} AI文字", template.getId());
            List<AiSynthesisResp> aiSynthesisTexts = aiClient.requestTextAi(aiTextList);
            log.info("正在处理 templateId: {} AI生成文字, {} 个", template.getId(), aiSynthesisTexts.size());

            // 4.3 请求AI抠图接口，扣取用户上传的图片
            log.info("正在处理 templateId: {}的抠图", template.getId());
            List<AiSynthesisResp> aiSegImages = new ArrayList<>();
            if (StringUtils.isNotEmpty(generateReqVO.getImageUrl())) {
                // 处理是否需要抠图处理逻辑
                if (generateReqVO.getIsSegmentation()) {
                    aiSegImages = requestAISegmentation(aiSegList, generateReqVO);
                } else {
                    for (AiSynthesisRqe reqItem : aiSegList) {
                        AiSynthesisResp respItem = BeanUtils.toBean(reqItem, AiSynthesisResp.class);
                        respItem.setImgUrl(generateReqVO.getImageUrl());
                        aiSegImages.add(respItem);
                    }
                }
            }
            log.info("正在处理 templateId: {} AI抠图, {} 个", template.getId(), aiSegImages.size());

            // 6. 将AI生成的内容替换模版内容
            aiSynthesisBg.forEach(item -> {
                imgDataPage.setBackgroundImage(item.getImgUrl());
            });
            aiSynthesisImages.forEach(item -> {
                synthesisUidMap.put(item.getUuid(), item);
            });
            aiSynthesisTexts.forEach(item -> {
                synthesisUidMap.put(item.getUuid(), item);
            });
            aiSegImages.forEach(item -> {
                synthesisUidMap.put(item.getUuid(), item);
            });
            imgWidgetBOList.forEach(item -> {
                if (synthesisUidMap.containsKey(item.getUuid())) {
                    AiSynthesisResp aiSynthesis = synthesisUidMap.get(item.getUuid());
                    if (item.getType() == ImgWidgetTypeEnum.IMG) {
                        if (item.getIsAI()) {
                            imgDataPage.setBackgroundImage(aiSynthesis.getImgUrl());
                        } else if (item.getIsSegmentation()) {
                            item.setImgUrl(aiSynthesis.getImgUrl());
                        }
                        // 当前的逻辑是 移除当前元素，设置背景 所以此处直接替换背景
                        // imgDataPage.setBackgroundImage(aiSynthesis.getImgUrl());
//                        item.setImgUrl(aiSynthesis.getImgUrl());
                    } else if (item.getType() == ImgWidgetTypeEnum.TEXT) {
                        item.setText(aiSynthesis.getText());
                    }
                }
            });

            // 填充至原始类中 为后面存储海报信息的data做准备
            // TODO 当前场景下，此处注意需要移除AI图片
            imgWidgetBOList.removeIf(item -> item.getIsAI() && item.getType() == ImgWidgetTypeEnum.IMG);
            PosterDO posterDO = createPosterDBByTemplate(template);
            posterDO.setData(JsonUtils.toJsonString(imgDataInfoBO));
            posterDO.setKeyword(generateReqVO.getKeyword());
            posterDO.setBizName(generateReqVO.getBizName());
            // 8. 保存模版至海报数据表中
            appPosterMapper.insert(posterDO);

            // 8.1. 渲染封面
            log.info("template_id: {}, 正在渲染 poster_id: {} 的封面", template.getId(), posterDO.getId());
            Path localPosterPath = drawClient.coverScreenshot(posterDO.getId());
            // TODO 可能在某些情况下海报封面生成失败，后续可以有定时任务监控
            if (null != localPosterPath) {
                String remotePath = imgClient.uploadCover(localPosterPath.toString());
                posterDO.setCover(remotePath);
                appPosterMapper.updateById(posterDO);
            }
            // 结果存储
            log.info("template_id: {}, poster_id: {} 封面渲染完成", template.getId(), posterDO.getId());
            posterResult.add(posterDO);
            // 海报ID用于反向存储在Submit表中
            posterIds.add(posterDO.getId());
        }
        // 9 创建提交申请至数据库(同时赋值生成的海报id)
        AppSubmitSaveReqVO submitDO = BeanUtils.toBean(generateReqVO, AppSubmitSaveReqVO.class);
        submitDO.setImage(generateReqVO.getImageUrl());
        submitDO.setPosterIds(StringUtils.join(posterIds, ","));
        appSubmitService.createSubmit(submitDO);

        // 10. 构建返回结果
        return posterResult.stream().map(
                item -> {
                    PosterGenerateRespVO respItem = BeanUtils.toBean(item, PosterGenerateRespVO.class);
                    respItem.setCover(imgClient.fixImgUrl(respItem.getCover()));
                    return respItem;
                }
        ).collect(Collectors.toList());
    }

    @Override
    public List<PosterGenerateRespVO> generatePosterPlus(PosterGeneratePlusReqVO generateReqVO) {
        // 结果返回只有base64图片信息，将该数据上传远程服务，然后替换为远程图片服务地址
        AiSynthesisRqe aiSynthesis = BeanUtils.toBean(generateReqVO, AiSynthesisRqe.class);
        if (!generateReqVO.getPosterSizeType().equals(PosterSizeEnum.CUSTOM)) {
            aiSynthesis.setPageWidth(generateReqVO.getPosterSizeType().getWidth());
            aiSynthesis.setPageHeight(generateReqVO.getPosterSizeType().getHeight());
            if (aiSynthesis.getPrompt() == null) {
                aiSynthesis.setPrompt("");
            }
            if (aiSynthesis.getNegativePrompt() == null) {
                aiSynthesis.setNegativePrompt("");
            }
        }

        List<PosterDO> posterResult = new ArrayList<>();
        List<AiSynthesisResp> respList = aiClient.requestImgAi(aiSynthesis);
        for (AiSynthesisResp item : respList) {
            String bgUrl = imgClient.uploadSegmentationBase64(item.getBase64(), "png");
            // 构建海报数据使用初始化模版
            PosterDO posterDO = createPosterDBByPlus(aiSynthesis, bgUrl);
            // 保存模版至海报数据表中
            appPosterMapper.insert(posterDO);

            // 渲染封面
            log.info("正在渲染 poster_id: {} 的封面", posterDO.getId());
            Path localPosterPath = drawClient.coverScreenshot(posterDO.getId());
            // TODO 可能在某些情况下海报封面生成失败，后续可以有定时任务监控
            if (null != localPosterPath) {
                String coverPath = imgClient.uploadCover(localPosterPath.toString());
                posterDO.setCover(coverPath);
                appPosterMapper.updateById(posterDO);
            }
            // 结果存储
            log.info("poster_id: {} 封面渲染完成", posterDO.getId());
            posterResult.add(posterDO);
        }
        // 10. 构建返回结果
        return posterResult.stream().map(
                item -> {
                    PosterGenerateRespVO respItem = BeanUtils.toBean(item, PosterGenerateRespVO.class);
                    respItem.setCover(imgClient.fixImgUrl(respItem.getCover()));
                    return respItem;
                }
        ).collect(Collectors.toList());
    }

    @Override
    public Long create(AppPosterSaveReqVO createReqVO) {
        // 插入
        PosterDO insert = BeanUtils.toBean(createReqVO, PosterDO.class);
        appPosterMapper.insert(insert);
        // 返回
        return insert.getId();
    }

    @Override
    public void update(AppPosterSaveReqVO updateReqVO) {
        // 校验存在
        validateExists(updateReqVO.getId());
        // 更新
        PosterDO updateObj = BeanUtils.toBean(updateReqVO, PosterDO.class);
        appPosterMapper.updateById(updateObj);
    }

    @Override
    public void delete(Long id) {
        // 校验存在
        validateExists(id);
        // 删除
        appPosterMapper.deleteById(id);
    }

    private void validateExists(Long id) {
        if (appPosterMapper.selectById(id) == null) {
            throw exception(POSTER_NOT_EXISTS);
        }
    }

    @Override
    public PosterDO get(Long id) {
        PosterDO posterDO = appPosterMapper.selectById(id);
        // 更新图片信息
        fixPosterDoUrl(posterDO);
        return posterDO;
    }

    @Override
    public PageResult<PosterDO> getPage(AppPosterPageReqVO pageReqVO) {
        return appPosterMapper.selectPage(pageReqVO);
    }

    /**
     * 对象中图片都是图片后缀，为了方便图床服务更新
     * 再此需要返回对应图片
     *
     * @param posterDO
     */
    private void fixPosterDoUrl(PosterDO posterDO) {
        if (null == posterDO) {
            return;
        }
        ImgDataInfoBO imgDataInfoBO = JsonUtils.parseObject(posterDO.getData(), ImgDataInfoBO.class);
        ImgPageBO imgDataPage = imgDataInfoBO.getPage();
        List<ImgWidgetBO> imgWidgetBOList = imgDataInfoBO.getWidgets();
        imgDataPage.setBackgroundImage(imgClient.fixImgUrl(imgDataPage.getBackgroundImage()));
        imgWidgetBOList.forEach(item -> {
            item.setImgUrl(imgClient.fixImgUrl(item.getImgUrl()));
        });
        posterDO.setCover(imgClient.fixImgUrl(posterDO.getCover()));
        posterDO.setData(JsonUtils.toJsonString(imgDataInfoBO));
    }


    /**
     * 处理base转换为图片服务器地址
     *
     * @param requestList 原始http请求返回地址
     * @return 上传base64后的列表
     */
    private List<AiSynthesisResp> requestAIImg(List<AiSynthesisRqe> requestList, PosterGenerateReqVO posterGenerateReqVO, ImgPageBO imgPageBO) {
        if (StringUtils.isEmpty(posterGenerateReqVO.getImageUrl())) {
            return new ArrayList<>();
        }
        // 获取上传文件的base64信息
        String uploadBase64 = imgClient.getImgBase64(posterGenerateReqVO.getImageUrl());
        String bgBase64 = imgClient.getImgBase64(imgPageBO.getBackgroundImage());
        for (AiSynthesisRqe reqItem : requestList) {
            reqItem.setImgBase64(uploadBase64);
            reqItem.setBgBase64(bgBase64);
        }
        // 结果返回只有base64图片信息，将该数据上传远程服务，然后替换为远程图片服务地址
        List<AiSynthesisResp> respList = aiClient.requestImgAi(requestList);
        for (AiSynthesisResp item : respList) {
            String remotePath = imgClient.uploadSegmentationBase64(item.getBase64(), "png");
            log.info("upload segImgPath : {}", remotePath);
            item.setImgUrl(remotePath);
        }
        return respList;
    }

    /**
     * 处理base转换为图片服务器地址
     *
     * @param requestList 原始http请求返回地址
     * @return 上传base64后的列表
     */
    private List<AiSynthesisResp> requestAISegmentation(List<AiSynthesisRqe> requestList, @Nullable PosterGenerateReqVO posterGenerateReqVO) {
        if (posterGenerateReqVO != null) {
            if (StringUtils.isEmpty(posterGenerateReqVO.getImageUrl())) {
                return new ArrayList<>();
            }
            // 获取上传文件的base64信息
            String uploadBase64 = imgClient.getImgBase64(posterGenerateReqVO.getImageUrl());
            for (AiSynthesisRqe reqItem : requestList) {
                reqItem.setImgBase64(uploadBase64);
            }
        }
        // 结果返回只有base64图片信息，将该数据上传远程服务，然后替换为远程图片服务地址
        List<AiSynthesisResp> respList = aiClient.requestSegmentation(requestList);
        for (AiSynthesisResp item : respList) {
            String remotePath = imgClient.uploadSegmentationBase64(item.getBase64(), "png");
            log.info("upload segImgPath : {}", remotePath);
            item.setImgUrl(remotePath);
        }
        return respList;
    }

    private PosterDO createPosterDBByTemplate(TemplateDO templateDO) {
        PosterDO posterDO = new PosterDO();
        posterDO.setData(templateDO.getData());
        posterDO.setWidth(templateDO.getWidth());
        posterDO.setHeight(templateDO.getHeight());
        posterDO.setKeyword(templateDO.getKeyword());
        posterDO.setBizName(templateDO.getBizName());
        posterDO.setTemplateId(templateDO.getId());
        posterDO.setTitle(templateDO.getTitle() + "_" + CommonUtils.generateShortUuid());
        return posterDO;
    }

    private PosterDO createPosterDBByPlus(AiSynthesisRqe synthesisRqe, String synthesisBgUrl) {
        ImgPageBO imgPageBO = new ImgPageBO();
        imgPageBO.setBackgroundImage(synthesisBgUrl);
        imgPageBO.setWidth(synthesisRqe.getPageWidth());
        imgPageBO.setHeight(synthesisRqe.getPageHeight());

        ImgDataInfoBO imgDataInfoBO = new ImgDataInfoBO();
        imgDataInfoBO.setWidgets(new ArrayList<>());
        imgDataInfoBO.setPage(imgPageBO);

        PosterDO posterDO = new PosterDO();
        posterDO.setData(JsonUtils.toJsonString(imgDataInfoBO));
        posterDO.setWidth(synthesisRqe.getPageWidth());
        posterDO.setHeight(synthesisRqe.getPageHeight());
        posterDO.setKeyword("通用");
        posterDO.setBizName("AI生成");
        posterDO.setTemplateId(0L);
        posterDO.setTitle("智能AI海报_" + CommonUtils.generateShortUuid());
        return posterDO;
    }
}
